package com.apes.framework.plugin.soe.core.service;

import com.apes.framework.jpa.ApesBean;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.soe.core.SceneEngine;
import com.apes.framework.plugin.soe.core.context.Command;
import com.apes.framework.plugin.soe.core.context.SoeContext;
import com.apes.framework.plugin.soe.metadata.*;
import com.apes.framework.plugin.soe.store.database.repository.SceneInstanceVariableRepository;
import com.apes.framework.plugin.soe.store.database.service.BusinessTypeService;
import com.apes.framework.util.JpaUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.framework.util.Tools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.script.ScriptException;
import java.util.*;

import static com.apes.framework.plugin.soe.core.utils.ScriptEvaluator.evalBool;

/**
 * 功能：执行服务
 *
 * @author xul
 * @create 2018-09-21 17:33
 */
@Service
public class ExecutionService {

    @Autowired
    private SceneEngine engine;

    @Autowired
    private BusinessTypeService businessTypeService;

    @Autowired
    private SceneInstanceVariableRepository sceneInstanceVariableRepository;

    public void execute() {
        try {
            while (true) {
                Command command = SoeContext.poll();
                if (command == null) {
                    break;
                }
                run(command.getEntity());
            }
        } finally {
            SoeContext.remove();
        }
    }

    public void consume(SimpleRequest request) {
        Object entity = request.getData();
        if (!check(entity)) return;
        run(entity);
    }

    public boolean check(Object entity) {
        Model model = filter(entity);
        if (model == null) return false;
        return true;
    }

    private void run(Object entity) {
        Model model = filter(entity);
        if (model == null) return;
        Scene scene = model.getScene();
        if (scene == null) return;
        if (!scene.check()) throw new RuntimeException("场景：" + scene.getId() + " 定义不合法");
        List<SceneFlow> sceneFlows = scene.getSceneFlows();
        if (sceneFlows == null || sceneFlows.isEmpty()) return;
        Map m = ReflectUtil.getMerge(model);
        mergeInstanceVariable(model, m);
        for (SceneFlow sceneFlow : sceneFlows) {
            if (!sceneFlow.isValid() || !sceneFlow.getSourceBusinessType().getId().equals(model.getInnerBusinessType()) || !eval(sceneFlow, model.getClass().getSimpleName(), m))
                continue;
            try {
                run(sceneFlow, model);
            } catch (Exception e) {
                throw new RuntimeException(String.format("场景流：%s，步骤：%s，异常！%s \n %s", sceneFlow.getScene().getId(), sceneFlow.getStep(), Tools.getThrowable(e).getMessage(), Tools.getAllStackTrace(e)));
            }
        }
    }

    private void mergeInstanceVariable(Model model, Map m) {
        BusinessType businessType = businessTypeService.getBusinessTypeByCache(model.getInnerBusinessType());
        if (businessType == null) {
            throw new RuntimeException("凭证类型：" + model.getInnerBusinessType() + " 未定义！");
        }
        SceneInstance sceneInstance = engine.getRuntimeService().findSceneInstance(model.getScene(), businessType, model.getInnerBusinessKey());
        if (sceneInstance == null) {
            return;
        }
        List<SceneInstanceVariable> sceneInstanceVariables = sceneInstanceVariableRepository.findAll(JpaDsl.toCriteriaByEq("sceneInstance", sceneInstance));
        sceneInstanceVariables.stream().forEach(variable -> m.put(variable.getName(), variable.getText()));
    }

    private void run(SceneFlow sceneFlow, Model model) {
        SceneInstance sceneInstance = engine.getRuntimeService().findSceneInstance(sceneFlow.getScene(), sceneFlow.getSourceBusinessType(), model.getInnerBusinessKey());
        if (sceneInstance != null && sceneInstance.isFinished()) return;
        if (sceneFlow.isStarted() || !sceneFlow.isAuto()) {
            /**场景实例创建**/
            if (sceneInstance == null) {
                sceneInstance = engine.getRuntimeService().startSceneInstance(sceneFlow.getScene());
            }
        } else {
            if (sceneInstance == null) {
                //throw new RuntimeException("场景：" + sceneFlow.getScene().getId() + " 实例不存在");
                return;
            }
        }

        /**任务节点执行**/
        if (sceneFlow.isOnce()) {
            /**检查节点是否已执行**/
            List<SceneInstanceFlow> instanceFlows = engine.getRuntimeService().findSceneInstanceFlow(sceneInstance, sceneFlow);
            if (instanceFlows != null && !instanceFlows.isEmpty()) {
                throw new RuntimeException("场景流：" + sceneFlow.getId() + " 不允许重复执行");
            }
        }

        /**创建场景实例流**/
        engine.getRuntimeService().createSceneInstanceFlow(sceneInstance, sceneFlow);

        /**创建场景实例角色（源）**/
        engine.getRuntimeService().createSceneInstanceRole(sceneInstance, sceneFlow.getSourceBusinessType(), model.getInnerBusinessKey());

        if (sceneFlow.isAuto()) {
            /**执行任务**/
            Object result = engine.getTaskService().complete(sceneFlow, model);
//            if ((result == null) || (result instanceof Collection && ((Collection) result).isEmpty())) {
//                throw new RuntimeException("场景：" + sceneFlow.getScene().getId() + " 执行错误");
//            }
            if (result != null) {
                createSceneInstanceRole(sceneInstance, result);
            }
        }

        /**标记场景实例已完成**/
        if (sceneFlow.isFinish()) {
            engine.getRuntimeService().finishedSceneInstance(sceneInstance);
        }
    }

    private void createSceneInstanceRole(SceneInstance sceneInstance, Object result) {
        if (result instanceof Collection) {
            ((Collection) result).stream().forEach(m -> createSceneInstanceRole(sceneInstance, m));
        } else {
            /**创建场景实例角色（目标）**/
            Object businessTypeId = null, businessKey = null;
            if (Model.class.isAssignableFrom(result.getClass())) {
                businessTypeId = ((Model) result).getInnerBusinessType();
                businessKey = ((Model) result).getInnerBusinessKey();
            } else if (Map.class.isAssignableFrom(result.getClass())) {
                businessTypeId = ((Map) result).get("innerBusinessType");
                businessKey = ((Map) result).get("innerBusinessKey");
            }
            if (!StringUtils.isEmpty(businessTypeId) && !StringUtils.isEmpty(businessKey)) {
                engine.getRuntimeService().createSceneInstanceRole(sceneInstance, businessTypeId.toString(), businessKey.toString());
            }
        }
    }

    private <T> T filter(Object entity) {
        /**实体不能为空**/
        if (entity == null) return null;

        /**Model实例**/
        if (!(Model.class.isAssignableFrom(entity.getClass()))) return null;

//        /**存在Business注解**/
//        if(ReflectUtil.getAnnotation(entity.getClass(), Business.class) == null) return null;

        /**Scene字段不能为空**/
        if (((Model) entity).getScene() == null) return null;

        return (T) entity;
    }

    /**
     * 表达式执行
     */
    private boolean eval(SceneFlow sceneFlow, String modelClassName, Map model) {
        String script = sceneFlow.buildScript();
        Map toMerge = getMerge(modelClassName, model, sceneFlow.getSceneFlowFactors());
        try {
            return evalBool(script, toMerge);
        } catch (ScriptException e) {
            e.printStackTrace();
            throw new RuntimeException("场景流：" + sceneFlow.getId() + " 执行表达式：" + script + " 错误");
        }
    }

    /**
     * 构造实例数据
     */
    private Map getMerge(String modelClassName, Map toMerge, Set<SceneFlowFactor> sceneFlowFactors) {
        sceneFlowFactors.stream().filter(sceneFlowFactor -> !toMerge.containsKey(sceneFlowFactor.getAttribute().getCode())).forEach(sceneFlowFactor -> {
            throw new RuntimeException("模型：" + modelClassName + " 中没有找到字段：" + sceneFlowFactor.getAttribute().getCode());
        });

        sceneFlowFactors.stream().forEach(sceneFlowFactor -> {
            String key = sceneFlowFactor.getAttribute().getCode();
            Object value = toMerge.get(key);
            if (value instanceof BusinessAttributeValue) {
                toMerge.put(key, ((BusinessAttributeValue) value).getCode().trim());
            } else if (value instanceof ApesBean) {
                value = JpaUtil.getIdToStr((ApesBean) value);
                toMerge.put(key, value);
            }
        });
        return toMerge;
    }


}
